package com.yys.apm.utils;


import com.yys.apm.model.CollectInfo;
import com.yys.apm.json.JsonWriter;
import com.yys.apm.logger.ILogger;
import com.yys.apm.logger.LoggerManager;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * 工具类
 *
 * @author Yingsheng Ye
 * @date 2021-03-19 17:01
 */
public class ApmAgentUtil {

    private static final ILogger logger = LoggerManager.getLogger(ApmAgentUtil.class);

    public static HttpServletRequest getHttpServletRequest() {
        HttpServletRequest httpServletRequest = null;
        try {
            httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        } catch (Exception e) {
            logger.info("get HttpServletRequest is null");
        }
        return httpServletRequest;
    }

    /**
     * object转json
     *
     * @param obj object
     * @return string
     */
    public static String objectToJson(Object obj) {
        Map<String, Object> item = new HashMap<>();
        item.put("TYPE", false);
        item.put(JsonWriter.SKIP_NULL_FIELDS, true);
        return JsonWriter.objectToJson(obj, item);
    }

    /**
     * 构建请求包
     *
     * @param collectInfo CollectInfo
     * @param serviceCode 服务编码
     * @param secretKey   秘钥
     * @return Map
     */
    public static Map<String, Object> buildRequestPkg(CollectInfo collectInfo, String serviceCode, String secretKey) {
        Map<String, Object> data = apmDataModelToMap(collectInfo);
        Map<String, Object> params = new HashMap<>();
        params.put("data", objectToJson(data));
        params.put("serviceCode", serviceCode);
        params.put("timestamp", System.currentTimeMillis());
        params.put("sign", sign(params, secretKey));
        return params;
    }

    /**
     * 构建请求包
     *
     * @param data        数据包
     * @param serviceCode 服务编码
     * @param secretKey   秘钥
     * @return Map
     */
    public static Map<String, Object> buildRequestPkg(String data, String serviceCode, String secretKey) {
        Map<String, Object> params = new HashMap<>();
        params.put("data", data);
        params.put("serviceCode", serviceCode);
        params.put("timestamp", System.currentTimeMillis());
        params.put("sign", sign(params, secretKey));
        return params;
    }

    /**
     * 发送http请求
     *
     * @param urlString 请求地址
     * @param params    参数
     * @return 返回String
     */
    public static String sendHttpRequest(String urlString, Map<String, Object> params) {
        try {
            URL url = new URL(urlString);
            // 组织请求参数
            StringBuilder postBody = new StringBuilder();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (entry.getValue() == null) continue;
                postBody.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), "utf-8")).append("&");
            }

            if (!params.isEmpty()) {
                postBody.deleteCharAt(postBody.length() - 1);
            }
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setConnectTimeout(1000);
            conn.setReadTimeout(3000);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.getOutputStream().write(postBody.toString().getBytes(StandardCharsets.UTF_8));
            conn.getOutputStream().flush();
            int responseCode = conn.getResponseCode();
            if (responseCode != 200) {
                System.err.println("send http request failed, response status:" + responseCode);
                return null;
            }

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                result.append(line).append("\n");
            }
            return result.toString().trim();
        } catch (Exception e) {
            logger.error(e, "send http request throw exception, details: " + e.getMessage());
        }
        return null;
    }

    /**
     * 获取主机IP
     *
     * @return String
     */
    public static String getHostIp() {
        try {
            InetAddress localhost = InetAddress.getLocalHost();
            return localhost.getHostAddress();
        } catch (UnknownHostException e) {
            logger.error(e, "get host throw exception:" + e.getMessage());
        }
        return null;
    }

    /**
     * 获取主机名称
     *
     * @return String
     */
    public static String getHostName() {
        try {
            InetAddress localhost = InetAddress.getLocalHost();
            return localhost.getHostName();
        } catch (UnknownHostException e) {
            logger.error(e, "get host throw exception:" + e.getMessage());
        }
        return null;
    }

    /**
     * ApmDataModel -> Map
     *
     * @param apmDataModel ApmDataModel
     * @return Map
     */
    private static Map<String, Object> apmDataModelToMap(CollectInfo apmDataModel) {
        Map<String, Object> map = new HashMap<>();
        Class<?> clazz = apmDataModel.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = null;
            try {
                value = field.get(apmDataModel);
            } catch (IllegalAccessException e) {
                System.err.println(e.getMessage());
            }
            map.put(fieldName, value);
        }
        for (Map.Entry<String, Object> entry : apmDataModel.entrySet()) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }

    /**
     * 签名
     *
     * @param data    Map
     * @param signKey 秘钥
     * @return String
     */
    private static String sign(Map<String, Object> data, String signKey) {
        List<String> keys = new ArrayList<>(data.keySet());
        Collections.sort(keys);
        StringBuilder content = new StringBuilder();
        content.append(signKey);
        for (String key : keys) {
            Object value = data.get(key);
            content.append(value);
        }
        String result = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(content.toString().getBytes());
            byte[] b = md.digest();
            int i;
            StringBuilder buf = new StringBuilder();
            for (byte value : b) {
                i = value;
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            logger.error(e, "sign error");
        }
        return result;
    }

    /**
     * id生成
     *
     * @return String
     */
    public static String generateId() {
        return UUID.randomUUID().toString().replaceAll("-", ".");
    }
}
